package ru.sgu.codecenter.fidx.command;

import org.apache.commons.codec.digest.DigestUtils;
import ru.sgu.codecenter.fidx.Configuration;
import ru.sgu.codecenter.fidx.OperationFailedException;
import ru.sgu.codecenter.fidx.model.FileDocument;
import ru.sgu.codecenter.fidx.model.HelpItem;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;

public class FindCommand extends Command {
    public static String SPECIAL_CHARACTERS = "+-!(){}[]^\"~*?:";

    @Override
    public String getName() {
        return "find";
    }

    @Override
    public List<HelpItem> getHelpItems() {
        List<HelpItem> helpItems = new ArrayList<HelpItem>();
        helpItems.add(new HelpItem(getName() + " <query>", "find files by \"<query>\" in content and file names, special characters are escaped"));
        helpItems.add(new HelpItem(getName() + " --query=<query>", "find files by <query>, special query syntax is accepted"));
        helpItems.add(new HelpItem(getName() + " --name=<file name>", "find files by exact name, extension is required"));
        helpItems.add(new HelpItem(getName() + " --path=<file path>", "find file by exact path"));
        helpItems.add(new HelpItem(getName() + " --path-prefix=<path>", "find file by prefix of path"));
        helpItems.add(new HelpItem(getName() + " --equal=<path>", "find files with the same SHA1 as file given by <path>"));
        return helpItems;
    }

    private String getFileSha1(String path) {
        File file = new File(path);
        if (!file.exists() || !file.isFile()) {
            throw new OperationFailedException("Unable to calculate SHA1 of file \"" + path + "\".");
        }

        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            String sha1 = DigestUtils.sha1Hex(fileInputStream);
            fileInputStream.close();
            return sha1;
        } catch (Exception e) {
            throw new OperationFailedException("Unable to calculate SHA1 of file \"" + path + "\".");
        }
    }

    private void displayResults(List<FileDocument> results) {
        int displayed = 0;
        for (FileDocument result : results) {
            displayed++;
            info("Relevant document \"" + result.getPath() + "\" found.");
            if (displayed > Configuration.getHitsCount()) {
                info("... and more.");
                break;
            }
        }

        if (results.isEmpty()) {
            info("No relevant documents.");
        }
    }

    private String preprocessArgument(String arg) {
        StringBuilder builder = new StringBuilder();
        for(int i = 0; i < arg.length(); i++) {
            if (SPECIAL_CHARACTERS.indexOf(arg.charAt(i)) >= 0) {
                builder.append("\\");
            }

            builder.append(arg.charAt(i));
        }

        return builder.toString();
    }

    @Override
    public void execute(String[] args) {
        for (String arg : args) {
            List<FileDocument> results = null;
            if (arg.startsWith("--substring=")) {
                String query = arg.substring("--substring=".length());
                if (query.isEmpty()) {
                    throw new OperationFailedException("Incorrect format of \"--substring\" command.");
                }
                results = engine.findByContentSubstring(query);
            }

            if (arg.startsWith("--query=")) {
                String query = arg.substring("--query=".length());
                if (query.isEmpty()) {
                    throw new OperationFailedException("Incorrect format of \"--query\" command.");
                }
                results = engine.findByContentQuery(query);
            }

            if (arg.startsWith("--name=")) {
                String query = arg.substring("--name=".length());
                if (query.isEmpty()) {
                    throw new OperationFailedException("Incorrect format of \"--name\" command.");
                }
                results = engine.findByFileName(query);
            }

            if (arg.startsWith("--path=")) {
                String query = arg.substring("--path=".length());
                if (query.isEmpty()) {
                    throw new OperationFailedException("Incorrect format of \"--path\" command.");
                }
                results = engine.findByFilePath(query);
            }

            if (arg.startsWith("--path-prefix=")) {
                String query = arg.substring("--path-prefix=".length());
                if (query.isEmpty()) {
                    throw new OperationFailedException("Incorrect format of \"--path-prefix\" command.");
                }
                results = engine.findByFilePathPrefix(query);
            }

            if (arg.startsWith("--equal=")) {
                String path = arg.substring("--equal=".length());
                results = engine.findBySha1(getFileSha1(path));
            }

            if (results == null) {
                arg = preprocessArgument(arg);
                try {
                    results = engine.findByFileName(arg);
                } catch (OperationFailedException e) {
                    results = new ArrayList<FileDocument>();
                }
                results.addAll(engine.findByContentSubstring(arg));
            }

            displayResults(results);
        }
    }
}
